// if(true){
//   // var foo = 'aaa'
//   let foo = 'aaa'
// }
// console.log(foo) // foo is not defined


// for (let i = 0; i < 3; i++) {
//   for (let i = 0; i < 3; i++) {
//     console.log(i)
//   }
// }

// var elements = [{}, {}, {}]
// for (var i = 0; i < elements.length; i++) {
//   elements[i].onclick = function () {
//     console.log(i)
//   }
// }
// elements[2].onclick()


// var elements = [{}, {}, {}]
// for (var i = 0; i < elements.length; i++) {
//   elements[i].onclick = (function (i) {
//     return function () {
//       console.log(i)
//     }
//   })(i)
// }
// elements[2].onclick()

// var elements = [{}, {}, {}]
// for (let i = 0; i < elements.length; i++) {
//   elements[i].onclick = function () {
//     console.log(i)
//   }
// }
// elements[2].onclick()

// for (let i = 0; i < 3; i++) {
//   let i = 'foo'
//   console.log(i)
// }


// let i = 0;

// if (i < 3) {
//   let i = 'foo'
//   console.log(i)
// }

// i++

// if (i < 3) {
//   let i = 'foo'
//   console.log(i)
// }

// i++

// if (i < 3) {
//   let i = 'foo'
//   console.log(i)
// }

// i++


// // 变量提升
// // foo已经被声明了，只是还没有赋值
// console.log(foo)
// var foo = 'aaa'

// // let 取消了变量提升
// console.log(bar)  // 控制台报错
// let bar = 'bar'


// let i = 1
// // let i = 2
// if(true){
//   let i = 2
// }


// const arr = [100, 200, 300]
// // 按照变量名的位置，分配数组中对应位置的值
// const [foo, bar, baz] = arr
// console.log(foo, bar, baz) // 100 200 300

// // 如果想获取最后一个值，可以把前边的变量删掉，但是要保留逗号
// const [, , f] = arr
// console.log(f) // 300


// // 变量前加 ...   表示提取从当前位置开始往后的所有成员，成员放在数组中
// // ... 的用法只能在最后位置使用
// const [a, ...rest] = arr
// console.log(rest) // [ 200, 300 ]

// // 如果变量少于数组长度，就会按顺序提取，后边的成员不会被提取
// const [y] = arr
// console.log(y) // 100


// // 如果变量长度大于数组长度，多出来的变量就是 undefined
// const [z, x, c, v] = arr
// console.log(v) // undefined

// // 可以给变量默认值，如果数组中没有这个成员，就用默认值
// const [q, w, e = 1, r = 2] = arr
// console.log(e, r) // 300 2



// const obj = { name: 'aaa', age: 18 }

// const { name } = obj
// console.log(name)

// const age = 20
// // 如果要提取的变量名已经在外部声明过，可以将变量赋值给另一个变量名
// // 可以设置默认值
// const { age: objAge = 33 } = obj
// console.log(age, objAge)  // 20 18

// const name = 'es2015'
// const str = `hello ${name},this is a \'string\'`
// console.log(str) // hello es2015,this is a 'string'


// // 可以给模板字符串添加一个 标签函数
// const str = console.log`hello world` // [ 'hello world' ]


// const name = 'tom'
// const gender = true
// // 定义一个标签函数
// // 标签函数的第一个参数是模板字符串中的内容 按照表达式 分割后的静态内容的数组
// // 后边的参数就是模板字符串中出现的表达式的变量
// // 返回值就是字符串最终的内容
// // 可以在标签函数中对变量进行加工
// function tag1(string, name, gender) {
//   console.log(string) // [ 'hey, ', ' is a ', '' ]
//   console.log(name, gender) // tom true
//   const sex = gender ? 'man' : 'woman'
//   return string[0] + name + string[1] + sex + string[2]
// }
// const str1 = tag1`hey, ${name} is a ${gender}`
// console.log(str1) // hey, tom is a man



// const message = 'Error: foo is not defined.'
// console.log(message.startsWith('Error')) // true
// console.log(message.endsWith('.'))  // true
// console.log(message.includes('foo'))  // true


// // 在此之前给函数参数设置默认值是在函数内部通过判断来给参数默认值
// function foo(enable) {
//   enable = enable === undefined ? true : enable
//   console.log(enable)
// }

// // 现在只需要在形参的后面设置一个 = 就可以设置默认值
// // 这个默认值在没有传递实参或者实参是undefined的时候使用
// // 如果传递多个参数，带默认值的参数放在最后,不然可能没法工作
// function foo(bar, enable = true) {
//   console.log(bar, enable) // 111 true
// }
// foo(111)


// // 以前接收未知个数的参数都通过 arguments， arguments 是给类数组
// function foo() {
//   console.log(arguments)
// }

// // ES2015 中新增了 ... 接收剩余参数
// // 形参以数组的形式接收从当前位置开始往后所有的实参
// // 只能出现在形参的最后一个，且只可以使用一次
// function foo(first, ...args) {
//   console.log(args)
// }
// foo(1, 2, 3, 4)


// const arr = ['foo', 'bar', 'baz']
// console.log.apply(console, arr) // es2015 前
// console.log(...arr) // es2015 后
// // foo bar baz


// const inc = n => n + 1
// console.log(inc(100))


// const foo = {
//   name: 'tom',
//   sayName: function () {
//     console.log(this.name)
//   },
//   sayName1: () => {
//     console.log(this.name)
//   },
//   sayNameAsync: function () {
//     setTimeout(() => {
//       console.log(this.name)
//     })
//   }
// }
// foo.sayName() // tom
// foo.sayName1() // undefined
// foo.sayNameAsync() // tom


// const bar = '456'
// const obj = {
//   foo: 123,
//   // 传统写法必须 : 后面跟上变量
//   // bar: bar
//   // es2015 这样的写法和上边等价
//   bar,
//   // 传统的方法后边用 : 跟一个 function
//   // method1: function(){},
//   // 现在省略 : 直接 (){} 和上边也是等价的
//   // 但是背后其实就是普通的 function,这里的this也会指向当前对象
//   method1() { },
//   // 之前表达式不能直接写在对象的属性里，需要obj[]的方式来声明
//   // 现在可以直接在变量中用 [] 来声明属性
//   [1 + 1]: 456,
// }
// // 之前表达式不能直接写在对象的属性里，需要obj[]的方式来声明
// obj[Math.random()] = 123


// const source1 = {
//   a: 123,
//   b: 123
// }
// const source2 = {
//   b: 789,
//   d: 789
// }

// const target = {
//   a: 456,
//   c: 456
// }
// const result = Object.assign(target, source1, source2)
// console.log(target)  // { a: 123, c: 456, b: 789, d: 789 }
// console.log(result === target) // true


// console.log(0 == false)  //true
// console.log(0 === false) // false
// console.log(+0 === -0) // true
// console.log(+0 === -0) // true
// console.log(NaN === NaN) // false

// console.log(Object.is(+0 === -0)) // false
// console.log(Object.is(NaN, NaN))  // true


// const person = {
//   name: 'aaa',
//   age: 20
// }
// // 创建一个Proxy 的实例，第一个参数为要操作的对象
// // 第二个参数也是一个对象 - 代理的处理对象
// // 通过 get 方法监视属性的访问，通过 set 方法监视设置属性的过程
// const personProxy = new Proxy(person, {
//   // get方法接收的参数分别为：目标对象， 访问的属性名
//   // 返回值作为外部访问这个属性的结果
//   get(target, property) {
//     // console.log(target, property)  // { name: 'aaa', age: 20 } name
//     // return 'zhangsan'
//     // 判断target中是否有当前属性，有就返回，没有返回 undefined 或默认值
//     return property in target ? target[property] : 'default'
//   },
//   // set 方法参数：目标对象，设置的属性，设置的值
//   set(target, property, value) {
//     // console.log(target, property, value) // { name: 'aaa', age: 20 } gender true
//     if (property === 'age') {
//       if (!Number.isInteger(value)) {
//         throw new TypeError(`${value} is not an int`)
//       }
//     }
//     target[property] = value
//   }
// })
// console.log(personProxy.name) // aaa
// console.log(personProxy.xxx) // default

// personProxy.gender = true
// // personProxy.age = '100' // TypeError: 100 is not an int
// personProxy.age = 100
// console.log(person) // { name: 'aaa', age: 100, gender: true }




// const person = {
//   name: 'aaa',
//   age: 20
// }

// const personProxy = new Proxy(person, {
//   // 外部对这个proxy对象进行delete操作的时候执行
//   // 参数：代理目标对象，要删除的属性名
//   deleteProperty(target, property){
//     console.log(target, property) // { name: 'aaa', age: 20 } age
//     delete target[property]
//   }
// })
// delete personProxy.age
// console.log(person) // { name: 'aaa' }


// const list = []
// const listProxy = new Proxy(list, {
//   set(target, property, value) {
//     console.log('set', property, value) // set 0 100
//     target[property] = value
//     return true //表示设置成功
//   }
// })

// listProxy.push(100)
// console.log(list)  // [ 100 ]


// const obj = {
//   foo:'123',
//   bar:'456'
// }
// const proxy = new Proxy(obj,{
//   // 没有添加处理对象的方法 等同于 将方法原封不动的交给 Reflect 执行
//   get(target, property){
//     return Reflect.get(target,property)
//   }
// })

// const obj = {
//   name: 'aaa',
//   age: 18
// }

// 操作对象方法不统一
// console.log('age' in obj)// 是否存在某个属性
// console.log(delete obj['age'])//删除属性
// console.log(Object.keys(obj))//获取属性名

// // 以上方法使用 Reflect 可以有统一的使用方法
// console.log(Reflect.has(obj, 'name'))
// console.log(Reflect.deleteProperty(obj, 'age'))
// console.log(Reflect.ownKeys(obj))


// const obj = {
//   name: 'aaa',
//   age: 18
// }
// console.log(Reflect.get(obj, 'name')) // aaa
// Reflect.set(obj, 'gender', 'man')
// console.log(obj) // { name: 'aaa', age: 18, gender: 'man' }


// function Person(name){
//   this.name = name
// }

// // new
// const person1 = new Person('zhangsan')

// // Reflect.construct
// const person2 = Reflect.construct(Person, ['zhangsan'])



// class 

// 通过定义一个函数，来定义一个类型
// function Person(name){
//   this.name = name
// }
// Person.prototype.say = function(){
//   console.log(`hi, my name is ${this.name}`)
// }

// es2015 使用 class 关键词声明一个类型
// class Person {
//   // constructor 是当前类型的构造函数
//   constructor(name) {
//     this.name = name
//   }
//   say() {
//     console.log(`hi, my name is ${this.name}`)
//   }
// }
// const p = new Person('tom')
// p.say()

// class Person {
//   constructor(name) {
//     this.name = name
//   }
//   //  使用 static 关键词定义静态方法
//   static create(name) {
//     return new Person(name)
//   }
// }
// // 直接使用类型来调用静态方法
// Person.create('tom')


// class Person {
//   constructor(name) {
//     this.name = name
//   }
//   say() {
//     console.log(`hi, my name is ${this.name}`)
//   }
// }
// // 使用 extends 继承父类
// // Student 继承自 Person，Student中就有Person中所有的成员
// class Student extends Person {
//   constructor(name,number){
//     // super 对象始终指向父类，调用它就是调用了父类的构造函数
//     super(name)
//     this.number = number
//   }
//   hello(){
//     // 可以使用 super 对象访问父类的成员
//     super.say()
//     console.log(`my school number is ${this.number}`)
//   }
// }
// const s = new Student('jack', '100')
// s.hello()
// // hi, my name is jack
// // my school number is 100


// // Set 是一个类型，通过创建它的实例就可以存放不重复的数据
// const s = new Set()

// // 通过 add方法往集合中添加数据，返回集合本身，所以还可以链式调用
// // 如果向集合中添加一个已经存在的值，则这个添加会被忽略
// s.add(1).add(2).add(3).add(2)
// console.log(s) // Set(3) { 1, 2, 3 }

// // 想要遍历集合中的数据可以使用集合的forEach方法
// s.forEach(v => console.log(v)) // 1 2 3
// // 遍历集合中的数据也可以使用es2015中的 for(...of...)
// for (let i of s) {
//   console.log(i)
// }
// //  1 2 3

// // 通过集合的 size 属性获取集合的长度，相当于数组中的length
// console.log(s.size) // 3

// // has 方法判断集合中是否存在某个值
// console.log(s.has(2))

// // delete 方法删除集合中的数据,删除成功返回 true
// console.log(s.delete(1))

// // clear 清除数组中的数据
// s.clear()
// console.log(s) // Set(0) {}

// // Set 最常用的是给数组去重
// const arr = [1, 2, 1, 3, 4, 2]
// // Set 的实例接受一个数组，数组里的值作为这个实例的初始值，重复的值会被忽略
// // 使用 Array.from() 方法或者 扩展运算符 将集合再次转换为数组
// // result = Array.from(new Set(arr))
// const result = [...new Set(arr)]
// console.log(result) // [ 1, 2, 3, 4 ]



// const obj = {}
// obj[true] = 'value'
// obj[123] = 'value'
// obj[{ a: 1 }] = 'value'
// obj[{ b: 2 }] = 'value'
// // 对象中的键：Boolear, Number, Object 类型的键都被转换为字符串
// // 问题：{ a: 1 } 和 { b: 2 } 两个不同的对象都被转换为 '[object Object]'
// console.log(Object.keys(obj)) // [ '123', 'true', '[object Object]' ]


// // 使用 new 创建一个 Map 的实例
// const m = new Map()

// const tom = { name: 'tom' }
// const jack = { name: 'jack' }

// // 可以使用实例的 set 方法为这个Map实例设置键值对
// m.set(tom, 90)
// m.set(jack, 95)
// console.log(m) // Map(2) { { name: 'tom' } => 90, { name: 'jack' } => 95 }
// // get 方法获取某个属性的值
// console.log(m.get(tom)) // 90
// // has 方法判断是否存在某个属性
// m.has(tom)
// // delete 方法删除某个属性
// m.delete(tom)
// // clear  方法清空Map实例中的属性
// // m.clear()

// // forEach 方法遍历这个实例对象
// m.forEach((value, key) => {
//   console.log(value, key) // 95 { name: 'jack' }
// })

// for (let i of m) {
//   console.log(i) // [ { name: 'jack' }, 95 ]
// }







// // shared.js ==========================
// const cache = {}

// // a.js ==============================
// cache['foo'] = Math.random()
// cache['a_foo'] = Math.random()
// // b.js ==============================
// cache['foo'] = '123'
// cache['b_foo'] = '123'

// console.log(cache) // { foo: '123' }

// // 使用Symbol函数就能创建一个Symbol类型的数据
// const s = Symbol()
// console.log(s) // Symbol()
// console.log(typeof s) // symbol
// console.log(Symbol() === Symbol()) // false

// console.log(Symbol('foo')) // Symbol(foo)
// console.log(Symbol('bar')) // Symbol(bar)


// const obj = {
//   [Symbol()]: '789'
// }
// obj[Symbol()] = '123'
// obj[Symbol()] = '456'

// console.log(obj) // { [Symbol()]: '789', [Symbol()]: '123', [Symbol()]: '456' }

// // 模拟实现对象私有成员
// // a.js =======================
// // 使用Symbol 创建私有成员的属性名
// // 在对象内部可以使用创建属性时的 Symbol 拿到对应的属性成员
// const name = Symbol()
// const person = {
//   [name]:'aaa',
//   say(){
//     console.log(this[name])
//   }
// }

// // b.js =======================
// // 在外部无法创建完全相同的 Symbol，所以无法直接访问这个 Symbol成员的属性，只能调用普通名词的成员
// person.say() /// aaa


// // Symbol 函数创建的值是唯一的，即使传了一样的参数也是唯一的
// console.log(Symbol('foo') === Symbol('foo')) // false
// // 如果想在全局复用一个相同的 Symbol 值，可以使用全局变量的方式实现，
// // 或者也可以使用 Symbol.for() 方法实现
// // Symbol.for() 方法传递一个字符串，传递相同的字符串就会返回相同的 Symbol 值
// // 这个方法内部维护了一个全局的注册表，为字符串和 Symbol 值提供了一个一一对应的关系
// // 需要注意的是，内部维护的是字符串和 Symbol 值对应的关系，所以如果 for 方法传的不是字符串类型，会转换成字符串类型
// const s1 = Symbol.for('foo')
// const s2 = Symbol.for('foo')
// console.log(s1 === s2) // true
// console.log(Symbol.for(true) === Symbol.for(true))

// console.log(Symbol.iterator) // Symbol(Symbol.iterator)
// console.log(Symbol.hasInstance) // Symbol(Symbol.hasInstance)


// const obj = {}
// console.log(obj.toString())

// const obj = {
//   [Symbol.toStringTag]: 'XObject'
// }
// console.log(obj.toString()) // [object XObject]


// const obj = {
//   [Symbol()]:'a',
//   foo:'123'
// }


// const set = new Set([1, 2, 3])
// const iterator = set[Symbol.iterator]()
// console.log(iterator.next())
// console.log(iterator.next())
// console.log(iterator.next())
// console.log(iterator.next())


// // 实现可迭代接口（Iterable）
// const obj = {
//   store: [1, 2, 3],
//   [Symbol.iterator]: function () {
//     let i = 0
//     const self = this
//     return {
//       next: function () {
//         return {
//           value: self.store[i],
//           done: i++ >= self.store.length
//         }
//       }
//     }
//   }
// }

// for (let item of obj) {
//   console.log(item)
// }



// function* foo() {
//   console.log('111')
//   yield 100
//   console.log('222')
//   yield 200
// }
// const result = foo()
// console.log(result.next()) // 111 { value: 100, done: false }
// console.log(result.next()) // 222 { value: 200, done: false }


// console.log(3 ** 2)

// const obj = {
//   a: 1, b: 2
// }
// console.log(Object.entries(obj))  //[ [ 'a', 1 ], [ 'b', 2 ] ]
// for (const [key, value] of Object.entries(obj)) {
//   console.log(key, value)
//   // a 1
//   // b 2
// }
// console.log(new Map(Object.entries(obj))) // Map(2) { 'a' => 1, 'b' => 2 }


// console.log('wl'.padEnd(10, '----------')) // wl--------
// console.log('102'.padStart(4, '0')) // 0102


// function(
//   a,
//   b,
// ) {

// }


// A段
// Promise.resolve().then(() => {
//   console.log(0)
//   return Promise.resolve(Promise.resolve(4))
// }).then(res => {
//   console.log(res)
// })

// // B段
// Promise.resolve().then(() => {
//   console.log(1)
// }).then(() => {
//   console.log(2)
// }).then(() => {
//   console.log(3)
// }).then(() => {
//   console.log(5)
// }).then(() => {
//   console.log(6)
// })

// 0 1 2 3 4 5 6



function fn(a, b, c) {
  return a + b + c
}

const f = fn.bind(this, 1)
f(1, 1)